import os
from functools import wraps
from typing import Iterable, Iterator, Optional, Callable, Type, Any

class FunctionDecoratorFactory:
    def __init__(self, 
                 do_result:Optional[Callable]=None,
                 do_except:Optional[Callable]=None, 
                 do_finally:Optional[Callable]=None,
                 do_pre_process:Optional[Callable]=None) -> None:
        empty_func = lambda *args, **kwargs:...
        empty_func2 = lambda x, *args, **kwargs:x
        self.do_result = do_result if do_result else empty_func2
        self.do_except = do_except if do_except else empty_func2
        self.do_finally = do_finally if do_finally else empty_func
        self.do_pre_process = do_pre_process if do_pre_process else empty_func

    def __call__(self) -> Callable[..., Any]:
        def wrapper(func):
            @wraps(func)
            def inner(*args, **kwargs):
                try:
                    self.do_pre_process(*args, **kwargs)
                    result = func(*args, **kwargs)
                    return self.do_result(result, *args, **kwargs)
                except Exception as e:
                    self.do_except(e, *args, **kwargs)
                    raise
                finally:
                    self.do_finally(*args, **kwargs)
            return inner
        return wrapper
    
def file_decorator(path_pos:int=0):
    '''
    功能：清理写入失败的文件
    参数：
        path_pos：路径参数在函数位置参数中的位置，从0开始
    '''
    def do_except(exception, *args, **kwargs):
        os.unlink(args[path_pos])
    return FunctionDecoratorFactory(do_except=do_except)()

def id_decorator(arg_pos:Optional[Iterable]=None, kwarg_keys:Optional[Iterable]=None):
    '''
    功能：id装饰器，加入输入参数的信息
    参数：
        id_pos：原id项位置
        arg_pos：位置参数索引
        kwarg_keys：关键字参数键
    '''
    extract = lambda x, y:[y[i] for i in x]
    def do_result(result, *args, **kwargs):
        arg_id = extract(arg_pos, args) if arg_pos else []
        kwarg_id = extract(kwarg_keys, kwargs) if kwarg_keys else []
        spec = '&'.join(str(i) for i in arg_id+kwarg_id)
        if isinstance(result, Iterator):
            for n, r in enumerate(list(result)):
                if isinstance(r, object):
                    r.nId = n + 1
                    r.spec = f'{spec}&{r.spec}' \
                        if hasattr(r, 'spec') and r.spec else spec
                    yield r
    return FunctionDecoratorFactory(do_result=do_result)()

def print_freq(content:str):
    freq = 0
    def do_pre_process(*args, **kwargs):
        nonlocal freq, content
        freq += 1
        print(content.format(freq))
    return FunctionDecoratorFactory(do_pre_process=do_pre_process)()

def partial_class_factory(cls:Type, **fixed_kwargs: Any) -> Callable:
    '''类工厂，固定初始化参数'''
    class FactoryClass(cls):
        def __init__(self, *args, **kwargs):
            all_kwargs = {**fixed_kwargs, **kwargs}
            super().__init__(*args, **all_kwargs)
    FactoryClass.__name__ = cls.__name__
    FactoryClass.__qualname__ = cls.__qualname__
    return FactoryClass
